#pragma once
#include <mutex>
#include <thread>

namespace JRG {
	template <class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		~auto_ptr()
		{
			delete _ptr;
		}
	private:
		T* _ptr;
	};
	void test_auto_ptr()
	{
		auto_ptr<int> ap1(new int(1));
		auto_ptr<int> ap2(new int(2));

		cout << *ap1 << endl;
		cout << *ap2 << endl;
	}

	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		unique_ptr(const unique_ptr<T>& s) = delete;
		unique_ptr<T>&  operator=(const unique_ptr<T>& s) = delete;
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		~unique_ptr()
		{
			delete _ptr;
		}
	private:
		T* _ptr;
	};

	template<class T>
	class shard_ptr
	{
	public:
		shard_ptr(T* ptr)
			:_ptr(ptr)
			,_count(new int(1))
		{}
		shard_ptr(const shard_ptr<T>& s)
			:_ptr(s._ptr)
			,_count(s._count)
		{
			(*_count)++;
		}
		shard_ptr<T>& operator=(const shard_ptr<T>& s)
		{
			if (s._ptr != _ptr)
			{
				if (--(*_count) == 0)
				{
					delete _ptr;
					delete _count;
				}
				_ptr = s._ptr;
				_count = s._count;
				(*_count)++;
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		~shard_ptr()
		{
			if (--(*_count) == 0)
			{
				delete _ptr;
				delete _count;
			}
		}
	private:
		T* _ptr;
		int* _count;
	};
	void shard_test()
	{
		shard_ptr<int> sp1(new int(1));
		shard_ptr<int> sp2(sp1);
		shard_ptr<int> sp3(nullptr);

		sp3 = sp2;
	}
}